package jsvm

import (
	"bytes"
	"encoding/binary"
	"got/conv"
	"io/ioutil"
	"sync"

	"github.com/robertkrimen/otto"
)

type JSVM struct {
	sync.Mutex            //继承互斥锁
	o          *otto.Otto //otto引擎
}

//创建一个javascrip
func New() *JSVM {
	obj := new(JSVM)
	obj.o = otto.New()
	obj.initFuncs()
	return obj
}

//初始化所有要使用的自定义函数
func (this *JSVM) initFuncs() {
	//number转换成byte类型
	this.Set("number2byte", func(call otto.FunctionCall) otto.Value {
		data, e1 := call.Argument(0).Export()
		if e1 != nil {
			v, _ := this.o.ToValue("param 1 error.")
			return v
		}
		toType, e2 := call.Argument(1).ToString()
		if e2 != nil {
			v, _ := this.o.ToValue("param 2 error.")
			return v
		}
		callback := func(data interface{}) otto.Value {
			b_buf := bytes.NewBuffer([]byte{})
			binary.Write(b_buf, binary.BigEndian, data)
			ret, _ := this.o.ToValue(b_buf.Bytes())
			return ret
		}
		switch toType {
		case "int8":
			r := conv.MustInt8(data, 0)
			return callback(&r)
		case "int16":
			r := int16(conv.MustInt32(data, 0))
			return callback(&r)
		case "int32":
			r := conv.MustInt32(data, 0)
			return callback(&r)
		case "int64":
			r := conv.MustInt64(data, 0)
			return callback(&r)
		case "uint8":
			r := conv.MustUint8(data, 0)
			return callback(&r)
		case "uint16":
			r := uint16(conv.MustUint32(data, 0))
			return callback(&r)
		case "uint32":
			r := conv.MustUint32(data, 0)
			return callback(&r)
		case "uint64":
			r := conv.MustUint64(data, 0)
			return callback(&r)
		case "float32":
			r := conv.MustFloat32(data, 0)
			return callback(&r)
		case "float64":
			r := conv.MustFloat64(data, 0)
			return callback(&r)
		}
		v, _ := this.o.ToValue("param 2 type error.")
		return v
	})
	//将byte转换成数字类型
	this.Set("byte2number", func(call otto.FunctionCall) otto.Value {
		data, e1 := call.Argument(0).Export()
		if e1 != nil {
			v, _ := this.o.ToValue("param 1 error.")
			return v
		}
		toType, e2 := call.Argument(1).ToString()
		if e2 != nil {
			v, _ := this.o.ToValue("param 2 error.")
			return v
		}
		btData, ok := data.([]byte)
		if !ok {
			v, _ := this.o.ToValue("data type error.")
			return v
		}
		callback := func(number interface{}) otto.Value {
			b_buf := bytes.NewBuffer(btData)
			binary.Read(b_buf, binary.BigEndian, number)
			ret, _ := this.o.ToValue(number)
			return ret
		}
		switch toType {
		case "int8":
			var r int8
			return callback(&r)
		case "int16":
			var r int16
			return callback(&r)
		case "int32":
			var r int32
			return callback(&r)
		case "int64":
			var r int64
			return callback(&r)
		case "uint8":
			var r uint8
			return callback(&r)
		case "uint16":
			var r uint16
			return callback(&r)
		case "uint32":
			var r uint32
			return callback(&r)
		case "uint64":
			var r uint64
			return callback(&r)
		case "float32":
			var r float32
			return callback(&r)
		case "float64":
			var r float64
			return callback(&r)
		case "string":
			v, _ := this.o.ToValue(string(btData))
			return v
		}
		v, _ := this.o.ToValue("param 2 type error.")
		return v
	})
}

//加载script脚本
func (this *JSVM) Load(script string) error {
	this.Lock()
	defer this.Unlock()
	_, e1 := this.o.Run(script)
	if e1 != nil {
		return e1
	}
	return nil
}

//从文件加载脚本
func (this *JSVM) LoadFile(file string) error {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}
	return this.Load(string(data))
}

//可以设置全局变量和函数
func (this *JSVM) Set(name string, value interface{}) error {
	this.Lock()
	defer this.Unlock()
	return this.o.Set(name, value)
}

//调用函数
func (this *JSVM) Call(name string, args ...interface{}) (v interface{}, err error) {
	this.Lock()
	defer this.Unlock()
	va, e1 := this.o.Call(name, nil, args...)
	if e1 != nil {
		return nil, e1
	}
	vb, e2 := va.Export()
	if e2 != nil {
		return nil, e2
	}
	return vb, nil
}
